﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.UI;
using System.Xml.Linq;
using System.Xml.XPath;
using Wowhead.Common;

namespace Wowhead.WebControls
{
    public class CharacterModelViewer : BaseModelViewer, INamingContainer
    {
        public string Name { get; set; }
        public string Realm { get; set; }
        public Region Region { get; set; }

        private ModelViewer _Model;

        private List<Pair<int,int>> _ItemList;
        private Gender _Gender;
        private Race _Race;

        private static int[] slotsToExclude = new int[] { 1, 10, 11, 12, 13, 17 };

        private IEnumerable<Pair<int, int>> ItemList
        {
            get 
            {
                if (_ItemList == null || _LastLoadedCharacter != GetCharacterHashCode())
                    this.Update();
                return _ItemList;
            }
        }

        private void Update()
        { 
            var document = GetArmoryXml();

            _Race = (Race)int.Parse(document.Element("characterInfo").Element("character").Attribute("raceId").Value);
            _Gender = (Gender)int.Parse(document.Element("characterInfo").Element("character").Attribute("genderId").Value);
            _LastLoadedCharacter = GetCharacterHashCode();

            _ItemList = (from element in document.XPathSelectElements("characterInfo/characterTab/items/item")
                         where !slotsToExclude.Contains(int.Parse(element.Attribute("slot").Value))
                         select GetWoWHeadItem(int.Parse(element.Attribute("id").Value))).ToList();
        }

        private int GetCharacterHashCode()
        {
            return this.Name.GetHashCode() ^ this.Realm.GetHashCode() ^ this.Region.GetHashCode();
        }

        protected override void OnInit(EventArgs e)
        {
            Page.RegisterRequiresControlState(this);
            this.EnableViewState = false;
            this.Load += new EventHandler(CharacterModelViewer_Load);
            base.OnInit(e);
        }

        void CharacterModelViewer_Load(object sender, EventArgs e)
        {
            if (!IsReady)
            {
                this.Visible = false;
                return;
            }
        }

        private bool IsReady
        {
            get 
            {
                return (!string.IsNullOrEmpty(this.Name) && !string.IsNullOrEmpty(this.Realm));
            }
        }

        public override void DataBind()
        {
            this.EnsureChildControls();
        }

        public override void RenderControl(HtmlTextWriter writer)
        {
            foreach (Control ctl in this.Controls)
                ctl.RenderControl(writer);
            //base.RenderControl(writer);
        }

        public void Redraw()
        {
            this.Update();
            this.DataBind();
        }

        protected override void CreateChildControls()
        {
            base.CreateChildControls();
            _Model = new ModelViewer();
            _Model.ID = "modelCharacter";
            _Model.DataIDField = "Value1";
            _Model.DataSlotField = "Value2";
            _Model.DataSource = ItemList;
            _Model.Race = _Race;
            _Model.Gender = _Gender;

            if (this.Width.HasValue)
                _Model.Width = this.Width.Value;
            
            if (this.Height.HasValue)
                _Model.Height = this.Height.Value;

            if (this.BackColor.HasValue)
                _Model.BackColor = this.BackColor.Value;

            _Model.DataBind();

            this.Controls.Add(_Model);
        }

        #region ControlState

        protected int _LastLoadedCharacter;

        protected override object SaveControlState()
        {
            object[] rawState = new object[5];
            rawState[0] = base.SaveControlState();
            rawState[1] = SerializeItemList();
            rawState[2] = (int)_Gender;
            rawState[3] = (int)_Race;
            rawState[4] = _LastLoadedCharacter;
            return rawState;
        }

        protected override void LoadControlState(object savedState)
        {
            object[] rawState = (object[])savedState;
            base.LoadControlState(rawState[0]);
            _ItemList = DeserializeItemList((string)rawState[1]); //(List<Pair<int, int>>)rawState[1];
            _Gender = (Gender)rawState[2];
            _Race = (Race)rawState[3];
            _LastLoadedCharacter = (int)rawState[4];
        }

        protected string SerializeItemList()
        {
            if (_ItemList == null || _ItemList.Count == 0)
                return "";

            return string.Join(";", (from item in _ItemList select string.Format("{0}:{1}", item.Value1, item.Value2)).ToArray());
        }

        protected List<Pair<int, int>> DeserializeItemList(string savedValue)
        {
            if (string.IsNullOrEmpty(savedValue))
                return null;

            string[] items = savedValue.Split(';');
            List<Pair<int, int>> res = new List<Pair<int, int>>();
            foreach (string item in items)
            {
                string[] values = item.Split(':');
                res.Add(new Pair<int, int>(int.Parse(values[0]), int.Parse(values[1])));
            }
            return res;
        }

        #endregion

        #region WebRequest methods

        protected XElement GetArmoryXml()
        {
            ArmoryEventArgs args = new ArmoryEventArgs()
            {
                Realm = this.Realm,
                Name = this.Name,
                Region = this.Region
            };

            System.Net.WebClient _wc = new System.Net.WebClient();
            _wc.QueryString.Add("r", this.Realm);
            _wc.QueryString.Add("n", this.Name);
            _wc.Headers.Add("user-agent", "MSIE 7.0");

            OnCharacterLoading(args);
            XElement res = XElement.Load(new System.Xml.XmlTextReader(_wc.OpenRead(string.Format("http://{0}.wowarmory.com/character-sheet.xml", this.Region.ToValueString()))));
            args.ArmoryDocument = res;
            OnCharacterLoaded(args);

            return res;
        }

        protected Pair<int,int> GetWoWHeadItem(int ID)
        {
            WowheadEventArgs args = new WowheadEventArgs() { ItemID = ID };
            
            OnItemLoading(args);
            XElement doc = Utils.GetWowheadXmlObject(WowheadObjectType.Item, ID);
            args.WowheadDocument = doc;
            OnItemLoaded(args);

            Pair<int, int> res = new Pair<int, int>()
            {
                Value1 = int.Parse(((IEnumerable)doc.XPathEvaluate("item/icon/@displayId")).Cast<XAttribute>().First().Value),
                Value2 = int.Parse(((IEnumerable)doc.XPathEvaluate("item/inventorySlot/@id")).Cast<XAttribute>().First().Value)
            };
            return res;
        }

        #endregion

        #region Events

        public event EventHandler<ArmoryEventArgs> CharacterLoading;
        public event EventHandler<ArmoryEventArgs> CharacterLoaded;
        public event EventHandler<WowheadEventArgs> ItemLoading;
        public event EventHandler<WowheadEventArgs> ItemLoaded;

        protected void OnCharacterLoading(ArmoryEventArgs e)
        { if (CharacterLoading != null) CharacterLoading(this, e); }

        protected void OnCharacterLoaded(ArmoryEventArgs e)
        { if (CharacterLoaded != null) CharacterLoaded(this, e); }

        protected void OnItemLoading(WowheadEventArgs e)
        { if (ItemLoading != null) ItemLoading(this, e); }

        protected void OnItemLoaded(WowheadEventArgs e)
        { if (ItemLoaded != null) ItemLoaded(this, e); }

        public class ArmoryEventArgs : EventArgs
        {
            public string Name { get; set; }
            public string Realm { get; set; }
            public Region Region { get; set; }
            public XElement ArmoryDocument { get; set; }
        }

        public class WowheadEventArgs : EventArgs
        {
            public int ItemID { get; set; }
            public XElement WowheadDocument { get; set; }
        }

        #endregion
    }
}
